package springfox.documentation.swagger.readers.parameter;

import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiParam;
import springfox.documentation.BeanUtils;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.EnumTypeDeterminer;
import springfox.documentation.spi.service.contexts.ParameterExpansionContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger.readers.parameter.SwaggerExpandedParameterBuilder;
import springfox.documentation.swagger.schema.ApiModelProperties;

import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
import static org.springframework.util.StringUtils.isEmpty;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER;
import static springfox.documentation.swagger.readers.parameter.Examples.examples;

public class SwaggerExpandedParameterBuilderNew extends SwaggerExpandedParameterBuilder {
    private final DescriptionResolver descriptions;
    private final EnumTypeDeterminer enumTypeDeterminer;

    public SwaggerExpandedParameterBuilderNew(DescriptionResolver descriptions, EnumTypeDeterminer enumTypeDeterminer) {
        super(descriptions, enumTypeDeterminer);
        this.descriptions = descriptions;
        this.enumTypeDeterminer = enumTypeDeterminer;
    }


    @Override
    public void apply(ParameterExpansionContext context) {
        Optional<ApiModelProperty> apiModelPropertyOptional = context.findAnnotation(ApiModelProperty.class);
        apiModelPropertyOptional.ifPresent(apiModelProperty -> fromApiModelProperty(context, apiModelProperty));
        Optional<ApiParam> apiParamOptional = context.findAnnotation(ApiParam.class);
        apiParamOptional.ifPresent(apiParam -> fromApiParam(context, apiParam));
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }

    private void fromApiParam(ParameterExpansionContext context, ApiParam apiParam) {
        String allowableProperty =
                ofNullable(apiParam.allowableValues())
                        .filter(((Predicate<String>) String::isEmpty).negate())
                        .orElse(null);
        AllowableValues allowable = allowableValues(
                ofNullable(allowableProperty),
                context.getFieldType().getErasedType());

        ParameterBuilder parameterBuilder = maybeSetParameterName(context, apiParam.name());
        parameterBuilder.description(descriptions.resolve(apiParam.value()))
                .defaultValue(apiParam.defaultValue())
//                .required(apiParam.required())
                .allowMultiple(apiParam.allowMultiple())
                .allowableValues(allowable)
                .parameterAccess(apiParam.access())
                .hidden(apiParam.hidden())
                .scalarExample(apiParam.example())
                .complexExamples(examples(apiParam.examples()))
                .order(SWAGGER_PLUGIN_ORDER)
                .build();

        // 如果  required 已经被其他Plugin设置为true了则就不读取RequestParam的required
        boolean required = (Boolean) BeanUtils.getFieldValue(parameterBuilder, "required");

        if (required) {
            return;
        }

        parameterBuilder.required(apiParam.required());


    }

    private void fromApiModelProperty(ParameterExpansionContext context, ApiModelProperty apiModelProperty) {
        String allowableProperty = ofNullable(apiModelProperty.allowableValues())
                .filter(((Predicate<String>) String::isEmpty).negate()).orElse(null);
        AllowableValues allowable = allowableValues(
                ofNullable(allowableProperty),
                context.getFieldType().getErasedType());

        ParameterBuilder parameterBuilder =maybeSetParameterName(context, apiModelProperty.name());
        parameterBuilder.description(descriptions.resolve(apiModelProperty.value()))
//                .required(apiModelProperty.required())
                .allowableValues(allowable)
                .parameterAccess(apiModelProperty.access())
                .hidden(apiModelProperty.hidden())
                .scalarExample(apiModelProperty.example())
                .order(SWAGGER_PLUGIN_ORDER)
                .build();
        // 如果  required 已经被其他Plugin设置为true了则就不读取RequestParam的required
        boolean required = (Boolean) BeanUtils.getFieldValue(parameterBuilder, "required");

        if (required) {
            return;
        }

        parameterBuilder.required(apiModelProperty.required());

    }

    private ParameterBuilder maybeSetParameterName(ParameterExpansionContext context, String parameterName) {
        if (!isEmpty(parameterName)) {
            context.getParameterBuilder().name(parameterName);
        }
        return context.getParameterBuilder();
    }

    private AllowableValues allowableValues(final Optional<String> optionalAllowable, Class<?> fieldType) {

        AllowableValues allowable = null;
        if (enumTypeDeterminer.isEnum(fieldType)) {
            allowable = new AllowableListValues(getEnumValues(fieldType), "LIST");
        } else if (optionalAllowable.isPresent()) {
            allowable = ApiModelProperties.allowableValueFromString(optionalAllowable.get());
        }
        return allowable;
    }

    private List<String> getEnumValues(final Class<?> subject) {
        return Stream.of(subject.getEnumConstants())
                .map((Function<Object, String>) Object::toString)
                .collect(toList());
    }



}
